package net.qqxh.service.impl;

import net.qqxh.common.utils.FileAnalysisTool;
import net.qqxh.persistent.FileNode;
import net.qqxh.persistent.JfSysUserData;
import net.qqxh.persistent.Jfile;
import net.qqxh.persistent.SearchLib;
import net.qqxh.service.FileEsService;
import net.qqxh.service.FileService;
import net.qqxh.service.common.BaseCallBack;
import net.qqxh.service.model.ResolveCallBackMsg;
import net.qqxh.service.task.FileReadTask;
import net.qqxh.service.task.FileResolveTask;
import net.qqxh.service.task.FileResolveTaskCallBack;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.HiddenFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.*;


@Service
public class FileServiceImpl implements FileService {


    @Autowired
    private TransportClient esclient;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${SUPPORT_EXTENSIONS}")
    private String[] supportExtensions;
    @Value("${jfile.auto_caching}")
    private boolean aotuCaching;
    @Autowired
    FileResolveTask fileResolveTask;

    @Autowired
    FileEsService fileEsService;
    @Autowired
    private RedisTemplate<String, FileNode> fileNodeRedisTemplate;
    @Autowired
    private FileReadTask fileReadTask;
    String fnrkey = "file_node_list";
    private Map<String, List<FileNode>> libs = new HashMap();


    @Override
    public List<Map> queryFileFromES(String esIndex, String keyword, String path, Integer esFrom, Integer esSize) {
        String highLightField = "content";
        QueryBuilder matchQueryc = QueryBuilders.matchPhraseQuery("content", keyword);
        QueryBuilder matchQueryn = QueryBuilders.matchPhraseQuery("name", keyword);
        QueryBuilder matchQueryPath = QueryBuilders.matchPhraseQuery("path", path);
        BoolQueryBuilder boolQueryMust1 = QueryBuilders.boolQuery();
        if (StringUtils.isNotEmpty(keyword)) {
            boolQueryMust1.should(matchQueryc);
            boolQueryMust1.should(matchQueryn);
        }
        BoolQueryBuilder boolQueryMust2 = QueryBuilders.boolQuery();
        if (StringUtils.isNotEmpty(path)) {
            boolQueryMust2.must(matchQueryPath);
        }
        BoolQueryBuilder boolQueryMust = QueryBuilders.boolQuery();
        boolQueryMust.must(boolQueryMust1).must(boolQueryMust2);
        HighlightBuilder hiBuilder = new HighlightBuilder();
        hiBuilder.preTags("<span class='high_light'>");
        hiBuilder.postTags("</span>");
        hiBuilder.field(highLightField);
        // 搜索数据
        SearchResponse response = esclient.prepareSearch(esIndex)
                .setQuery(boolQueryMust).setFrom(esFrom).setSize(esSize)
                .highlighter(hiBuilder)
                .execute().actionGet();
        //获取查询结果集
        SearchHits searchHits = response.getHits();
        logger.info("共搜到:" + searchHits.getTotalHits() + "条结果!");
        List<Map> list = new ArrayList();
        for (SearchHit searchHit : searchHits.getHits()) {
            Map jfileMap = searchHit.getSourceAsMap();
            HighlightField highlightField = searchHit.getHighlightFields().get(highLightField);
            Text[] text = highlightField == null ? new Text[]{} : highlightField.getFragments();
            StringBuffer content = new StringBuffer();
            for (Text str : text) {
                content.append(str);
            }
            String textContent = content.toString();
            jfileMap.put("text", textContent);
            jfileMap.put("rid", searchHit.getId());
            list.add(jfileMap);
        }
        return list;
    }

    @Override
    public void resolveAllFile(SearchLib searchLib, FileResolveTaskCallBack fileResolveTaskCallBack) throws IOException {
        /*删除索引*/
        ResolveCallBackMsg msg = new ResolveCallBackMsg();
        msg.setFinish(false);
        msg.setLevel("info");
        try {
            deleteIndex(searchLib);
            msg.setMsg("删除es索引成功");
            fileResolveTaskCallBack.doCallBack(msg);
        } catch (Exception e) {
            msg.setLevel("error");
            msg.setMsg("删除es索引失败");
            fileResolveTaskCallBack.doCallBack(msg);
        }
        try {
            msg.setMsg("创建es索引成功");
            fileEsService.createFileIndex(searchLib);
            fileResolveTaskCallBack.doCallBack(msg);
        } catch (Exception e) {
            msg.setLevel("error");
            logger.error("创建es索引失败", e.fillInStackTrace());
            msg.setMsg("创建es索引失败,请检查是否es是否启动。");
            msg.setFinish(true);
            fileResolveTaskCallBack.doCallBack(msg);
            return;
        }
        try {
            msg.setMsg("刷新缓存成功！");
            flushAllRedis();
            resolveFliesList2TreeNode();
            fileResolveTaskCallBack.doCallBack(msg);
        } catch (Exception e) {
            logger.error("刷新缓存失败", e.fillInStackTrace());
            msg.setFinish(true);
            msg.setLevel("error");
            msg.setMsg("刷新缓存失败，请检查redis是否启动！");
            fileResolveTaskCallBack.doCallBack(msg);
            return;
        }

        msg.setMsg("请稍后即将开始处理文件...");
        fileResolveTaskCallBack.doCallBack(msg);
        resolveFileByPath(searchLib, searchLib.getFileSourceDir(), fileResolveTaskCallBack);
    }


    @Override
    public void resolveFileByPath(SearchLib searchLib, String path, FileResolveTaskCallBack fileResolveTaskCallBack) throws IOException {
        File file = new File(path);
        if (file.isDirectory()) {
            /*进行递归*/
            IOFileFilter dirFileFilter = HiddenFileFilter.VISIBLE;
            Collection<File> files = FileUtils.listFiles(file, FileAnalysisTool.getIOFileFilter(supportExtensions), dirFileFilter);
            Integer totalCount = files.size();
            if(totalCount==0){
                ResolveCallBackMsg msg = new ResolveCallBackMsg();
                msg.setLevel("error");
                msg.setMsg("支持文件数量为0");
                msg.setFinish(true);
                fileResolveTaskCallBack.doCallBack(msg);
            }
            Iterator<File> iterator = files.iterator();
            int nowCount = 1;
            do {
                if (iterator.hasNext()) {
                    /*过滤掉window系统临时文件*/
                    File f = iterator.next();
                    if (f.getName().startsWith("~$")) {
                        totalCount--;
                        continue;
                    }
                    Jfile jfile = new Jfile(f);
                    fileResolveTask.doFileResolve(searchLib, jfile, fileResolveTaskCallBack, totalCount, nowCount);
                    nowCount++;
                }
            } while (iterator.hasNext());
        } else {
            Jfile jfile = new Jfile(file);
            fileResolveTask.doFileResolve(searchLib, jfile, fileResolveTaskCallBack, 1, 1);
        }
    }

    @Override
    public String deleteIndex(SearchLib searchLib) {

        IndicesExistsRequest request = new IndicesExistsRequest(searchLib.getEsIndex());
        IndicesExistsResponse response = esclient.admin().indices().exists(request).actionGet();
        if (response.isExists()) {
            DeleteIndexResponse dResponse = esclient.admin().indices().prepareDelete(searchLib.getEsIndex())
                    .execute().actionGet();
        }
        return "success";
    }


    /**
     * 清空redis缓存
     *
     * @return
     */
    @Override
    public String flushAllRedis() {
        redisConnectionFactory.getConnection().flushAll();
        return "success";
    }


    @Override
    public Object readFile2FormatTree(String key) {
        return libs.get(key);
    }


    @Override
    public boolean findFileByViewPath(SearchLib searchLib, String viewPath) {
        String src = FileAnalysisTool.getFileSourceByView(searchLib.getFileSourceDir(), searchLib.getFileViewDir(), viewPath);
        File file = new File(src);
        if (FileAnalysisTool.getIOFileFilter(supportExtensions).accept(file)) {
            return file.exists();
        } else {
            return true;
        }

    }

    public void cleanRubbish(SearchLib searchLib, BaseCallBack baseCallBack) {
        long start = System.currentTimeMillis();
        File dir = new File(searchLib.getFileViewDir());
        if (!dir.isDirectory()) {
            return;
        }
        IOFileFilter fileFilter = TrueFileFilter.INSTANCE;
        IOFileFilter dirFilter = HiddenFileFilter.VISIBLE;
        Collection<File> files = FileUtils.listFiles(dir, fileFilter, dirFilter);
        int totalCount = files.size() + 1;
        Iterator<File> iterator = files.iterator();
        do {
            if (iterator.hasNext()) {
                File file = iterator.next();
                String viewPath = file.getPath();
                Map msg = new HashMap<>();
                if (findFileByViewPath(searchLib, viewPath)) {
                    msg.put("status", 2);
                } else {
                    if (file.delete()) {
                        /*删除es、redis数据*/
                        String srcPath = FileAnalysisTool.getFileSourceByView(searchLib.getFileSourceDir(), searchLib.getFileViewDir(), viewPath);
                        List<Jfile> list = fileEsService.findFileByPath(searchLib, srcPath);
                        for (Jfile oldJfile : list) {
                            fileEsService.deleteFileFromEs(searchLib.getEsIndex(), oldJfile.getRid());
                            stringRedisTemplate.delete(oldJfile.getRid());
                        }

                        logger.info(file.getPath() + "删除成功");
                        msg.put("status", 1);
                    } else {
                        logger.info(file.getPath() + "删除失败");
                        msg.put("status", 0);
                    }
                }
                msg.put("totalCount", totalCount);
                msg.put("path", viewPath);
                if (baseCallBack != null) {
                    baseCallBack.doCallBack(msg);
                }
            }
        } while (iterator.hasNext());

        cleanDirectory(dir);

        long end = System.currentTimeMillis();
        Map msg = new HashMap<>();
        msg.put("totalCount", totalCount);
        msg.put("timeCost", end - start);
        if (baseCallBack != null) {
            baseCallBack.doCallBack(msg);
        }
    }

    /**
     * 清理空文件夹
     *
     * @param dir
     */
    private void cleanDirectory(File dir) {
        File[] dirs = dir.listFiles();
        for (File file : dirs) {
            if (file.isDirectory()) {
                cleanDirectory(file);
            }
        }
        if (dir.isDirectory()) {
            if (dir.delete()) {
                logger.info(dir.getPath() + "删除成功");
            }
        }
    }

    /**
     * 每30分钟刷新一次缓存
     *
     * @return
     */
    @Override
    @Scheduled(fixedRate = 1000 * 60 * 30)
    public void resolveFliesList2TreeNode() {
        if (!aotuCaching) {
            aotuCaching = true;
            return;
        }

        Map<String, SearchLib> searchLibMap = JfSysUserData.getSearchLibMap();
        Map<String, List<FileNode>> newlibs = new HashMap();
        for (Map.Entry entry : searchLibMap.entrySet()) {
            SearchLib searchLib = (SearchLib) entry.getValue();
            File file = new File(searchLib.getFileSourceDir());
            if (!file.exists()) {
                file.mkdir();
            }
            FileNode fileNode = new FileNode(new File(searchLib.getFileSourceDir()));
            List<FileNode> newTops = new ArrayList();
            fileReadTask.readFile2FmartDate(fileNode, newTops, FileAnalysisTool.getIOFileFilter(supportExtensions), fileReadTask, searchLib.getFileSourceDir());
            newlibs.put(searchLib.getFileSourceDir(), newTops);
        }
        libs = newlibs;
    }

    @Override
    public void addNode2cache(FileNode fileNode, SearchLib searchLib) {
        List<FileNode> tops = libs.get(searchLib.getFileSourceDir());
        if (fileNode.getParentId().equals(searchLib.getFileSourceDir())) {
            tops.add(fileNode);
        } else {
            addNode2cache(fileNode, tops);
        }
        if (!fileNode.getIsParent()) {
            Jfile jfile = new Jfile(new File(fileNode.getId()));
            try {
                fileResolveTask.doFileResolve(searchLib, jfile, null, 1, 1);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void addNode2cache(FileNode fileNode, List<FileNode> list) {
        for (FileNode fn : list) {
            if (fileNode.getId().contains(fn.getId())) {
                if (fn.getId().equals(fileNode.getParentId())) {
                    if (fn.getSons() == null) {
                        fn.setSons(new ArrayList<>());
                    }
                    fn.getSons().add(fileNode);
                    return;
                }
                List<FileNode> sons = fn.getSons();
                addNode2cache(fileNode, sons);
            }
        }
    }

    /**
     * 每30分钟清理一次脏数据
     *
     * @return
     */
    @Scheduled(fixedRate = 1000 * 60 * 30)
    public void clearTask() {
        Map<String, SearchLib> searchLibMap = JfSysUserData.getSearchLibMap();
        for (Map.Entry entry : searchLibMap.entrySet()) {
            SearchLib searchLib = (SearchLib) entry.getValue();
            cleanRubbish(searchLib, null);
            logger.info("正在清理垃圾数据....");
        }
    }


    @Override
    public void sendToDesktop(SearchLib searchLib, String path, String userid) {
        FileNode fn = new FileNode(new File(path));
        removeFromDesktop(searchLib, path, userid);
        fileNodeRedisTemplate.opsForList().leftPush(fnrkey + "-" + userid, fn);
    }

    @Override
    public void removeFromDesktop(SearchLib searchLib, String path, String userid) {
        FileNode fn;
        if (path == null) {
            fn = new FileNode();

        } else {
            fn = new FileNode(new File(path));
        }

        Long res = fileNodeRedisTemplate.opsForList().remove(fnrkey + "-" + userid, 1, fn);

    }

    @Override
    public List<FileNode> getMyDesktop(String userid) {
        long size = fileNodeRedisTemplate.opsForList().size(fnrkey + "-" + userid);
        List<FileNode> nodes;
        if (size == 0) {
            nodes = new ArrayList<>();
        } else {
            nodes = fileNodeRedisTemplate.opsForList().range(fnrkey + "-" + userid, 0, size);
        }
        return nodes;
    }

    @Override
    public void clearDesktop(String userid) {
        fileNodeRedisTemplate.delete(fnrkey + "-" + userid);
    }
}
